﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing;
using System.Reflection;
using NetPE.Core;
using System.Globalization;
using NetPE.Dialogs;
using System.Collections.ObjectModel;

namespace NetPE
{
    public class PropertyAction
    {
        string n;
        public string Name { get { return n; } set { n = value; } }
        EventHandler act;
        public EventHandler Callback { get { return act; } set { act = value; } }
    }
    public class PropertyEditor : BaseEditor
    {
        TableLayoutPanel panel;
        FlowLayoutPanel actPanel;

        List<PropertyAction> acts = new List<PropertyAction>();
        public List<PropertyAction> Actions { get { return acts; } }

        public PropertyEditor()
        {
            this.DoubleBuffered = true;
            panel = new TableLayoutPanel();
            panel.ColumnCount = 2;
            panel.Location = Point.Empty;
            panel.AutoSize = true;
            panel.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 200));
            panel.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 200));
            panel.CellBorderStyle = TableLayoutPanelCellBorderStyle.Single;
            panel.Padding = new Padding(5);

            actPanel = new FlowLayoutPanel();
            actPanel.AutoScroll = true;
            actPanel.FlowDirection = FlowDirection.TopDown;
            actPanel.Padding = new Padding(5);
            actPanel.Dock = DockStyle.Fill;

            this.Controls.Add(actPanel);
            actPanel.Controls.Add(panel);
        }
        protected override void RefreshData()
        {
            this.SuspendLayout();
            actPanel.SuspendLayout();
            panel.SuspendLayout();

            actPanel.Controls.Clear();
            actPanel.Controls.Add(panel);
            panel.Controls.Clear();

            PropertyInfo[] info = Object.GetType().GetProperties();
            panel.RowCount = 1;
            for (int i = 0; i < info.Length; i++) if (info[i].DeclaringType != Object.GetType() || ((!info[i].CanRead || !info[i].CanWrite) && info[i].PropertyType.BaseType != null && info[i].PropertyType.BaseType.Name != typeof(Collection<>).Name) || info[i].Name == "Item") continue; else panel.RowCount++;

            panel.Controls.Add(new Label() { Text = "Name", Dock = DockStyle.Fill, AutoSize = false, TextAlign = ContentAlignment.MiddleCenter }, 0, 0);
            panel.Controls.Add(new Label() { Text = "Value", Dock = DockStyle.Fill, AutoSize = false, TextAlign = ContentAlignment.MiddleCenter }, 1, 0);
            int ii = 0;
            for (int i = 0; i < info.Length; i++)
            {
                if (info[i].DeclaringType != Object.GetType() || ((!info[i].CanRead || !info[i].CanWrite) && info[i].PropertyType.BaseType != null && info[i].PropertyType.BaseType.Name != typeof(Collection<>).Name) || info[i].Name == "Item") continue; else ii++;
                panel.Controls.Add(new Label() { Text = info[i].Name, Dock = DockStyle.Fill, AutoSize = false, TextAlign = ContentAlignment.MiddleCenter }, 0, ii);

                TextBox tb;
                switch (info[i].PropertyType.FullName)
                {
                    case "NetPE.Core.Rva":
                        tb = new TextBox();
                        tb.BackColor = Color.Black;
                        tb.ForeColor = Color.White;
                        tb.BorderStyle = BorderStyle.None;
                        tb.TextAlign = HorizontalAlignment.Center;
                        tb.Width = 200;
                        tb.Anchor = AnchorStyles.None;
                        tb.Text = info[i].GetValue(Object, null).ToString();
                        tb.LostFocus += new EventHandler(PropChanged);
                        tb.Tag = info[i];
                        panel.Controls.Add(tb, 1, ii);
                        break;
                    case "System.String":
                        tb = new TextBox();
                        tb.BackColor = Color.Black;
                        tb.ForeColor = Color.White;
                        tb.BorderStyle = BorderStyle.None;
                        tb.TextAlign = HorizontalAlignment.Center;
                        tb.Width = 200;
                        tb.Anchor = AnchorStyles.None;
                        tb.Text = info[i].GetValue(Object, null).ToString();
                        tb.LostFocus += new EventHandler(PropChanged);
                        tb.Tag = info[i];
                        panel.Controls.Add(tb, 1, ii);
                        break;
                    case "System.Byte":
                        tb = new TextBox();
                        tb.BackColor = Color.Black;
                        tb.ForeColor = Color.White;
                        tb.BorderStyle = BorderStyle.None;
                        tb.TextAlign = HorizontalAlignment.Center;
                        tb.Width = 200;
                        tb.Anchor = AnchorStyles.None;
                        tb.Text = ((byte)info[i].GetValue(Object, null)).ToString("x2");
                        tb.LostFocus += new EventHandler(PropChanged);
                        tb.Tag = info[i];
                        panel.Controls.Add(tb, 1, ii);
                        break;
                    case "System.UInt16":
                        tb = new TextBox();
                        tb.BackColor = Color.Black;
                        tb.ForeColor = Color.White;
                        tb.BorderStyle = BorderStyle.None;
                        tb.TextAlign = HorizontalAlignment.Center;
                        tb.Width = 200;
                        tb.Anchor = AnchorStyles.None;
                        tb.Text = ((ushort)info[i].GetValue(Object, null)).ToString("x4");
                        tb.LostFocus += new EventHandler(PropChanged);
                        tb.Tag = info[i];
                        panel.Controls.Add(tb, 1, ii);
                        break;
                    case "System.UInt32":
                        tb = new TextBox();
                        tb.BackColor = Color.Black;
                        tb.ForeColor = Color.White;
                        tb.BorderStyle = BorderStyle.None;
                        tb.TextAlign = HorizontalAlignment.Center;
                        tb.Width = 200;
                        tb.Anchor = AnchorStyles.None;
                        tb.Text = ((uint)info[i].GetValue(Object, null)).ToString("x8");
                        tb.LostFocus += new EventHandler(PropChanged);
                        tb.Tag = info[i];
                        panel.Controls.Add(tb, 1, ii);
                        break;
                    case "System.UInt64":
                        tb = new TextBox();
                        tb.BackColor = Color.Black;
                        tb.ForeColor = Color.White;
                        tb.BorderStyle = BorderStyle.None;
                        tb.TextAlign = HorizontalAlignment.Center;
                        tb.Width = 200;
                        tb.Anchor = AnchorStyles.None;
                        tb.Text = ((ulong)info[i].GetValue(Object, null)).ToString("x16");
                        tb.LostFocus += new EventHandler(PropChanged);
                        tb.Tag = info[i];
                        panel.Controls.Add(tb, 1, ii);
                        break;
                    case "System.DateTime":
                        DateTimePicker pkr = new DateTimePicker();
                        pkr.BackColor = Color.Black;
                        pkr.ForeColor = Color.White;
                        pkr.Format = DateTimePickerFormat.Custom;
                        pkr.CustomFormat = "dd/MM/yyyy hh:mm:ss";
                        pkr.Width = 200;
                        pkr.Anchor = AnchorStyles.None;
                        pkr.Value = (DateTime)info[i].GetValue(Object, null); 
                        pkr.LostFocus += new EventHandler(PropChanged);
                        pkr.Tag = info[i];
                        panel.Controls.Add(pkr, 1, ii);
                        break;
                    default:
                        Button btn = new Button();
                        btn.FlatStyle = FlatStyle.Popup;
                        btn.Anchor = AnchorStyles.None;
                        btn.Dock = DockStyle.Fill;
                        btn.Text = info[i].PropertyType.Name;
                        btn.Click += new EventHandler(ShowEditDialog);
                        btn.Tag = info[i];
                        panel.Controls.Add(btn, 1, ii);
                        break;
                }
            }
            for (int i = 0; i < panel.RowCount; i++)
                panel.RowStyles.Add(new RowStyle(SizeType.Absolute, 32));

            foreach (PropertyAction act in acts)
            {
                Button btn = new Button();
                btn.Text = act.Name;
                btn.Click += new EventHandler(ActionDispatcher);
                btn.FlatStyle = FlatStyle.Popup;
                btn.AutoSize = true;
                btn.Tag = act.Callback;
                actPanel.Controls.Add(btn);
            }


            panel.ResumeLayout();
            actPanel.ResumeLayout();
            this.ResumeLayout();
        }

        bool onevent = false;
        private void PropChanged(object sender, EventArgs e)
        {
            if (!onevent)
            {
                PropertyInfo prop = (sender as Control).Tag as PropertyInfo;
                string val = (sender as Control).Text;

                switch (prop.PropertyType.FullName)
                {
                    case "NetPE.Core.Rva":
                        if (val.TrimStart('0').Length > 8) break;
                        prop.SetValue(Object, (Rva)uint.Parse(val, NumberStyles.HexNumber), null);
                        break;
                    case "System.String":
                        prop.SetValue(Object, val, null);
                        break;
                    case "System.Byte":
                        if (val.TrimStart('0').Length > 2) break;
                        prop.SetValue(Object, byte.Parse(val, NumberStyles.HexNumber), null);
                        break;
                    case "System.UInt16":
                        if (val.TrimStart('0').Length > 4) break;
                        prop.SetValue(Object, ushort.Parse(val, NumberStyles.HexNumber), null);
                        break;
                    case "System.UInt32":
                        if (val.TrimStart('0').Length > 8) break;
                        prop.SetValue(Object, uint.Parse(val, NumberStyles.HexNumber), null);
                        break;
                    case "System.UInt64":
                        if (val.TrimStart('0').Length > 16) break;
                        prop.SetValue(Object, ulong.Parse(val, NumberStyles.HexNumber), null);
                        break;
                    case "System.DateTime":
                        prop.SetValue(Object, (sender as DateTimePicker).Value, null);
                        break;
                    default: throw new InvalidOperationException();
                }

                onevent = true;
                switch (prop.PropertyType.FullName)
                {
                    case "NetPE.Core.Rva":
                        (sender as Control).Text = prop.GetValue(Object, null).ToString();
                        break;
                    case "System.String":
                        (sender as Control).Text = prop.GetValue(Object, null).ToString();
                        break;
                    case "System.Byte":
                        (sender as Control).Text = ((byte)prop.GetValue(Object, null)).ToString("x2");
                        break;
                    case "System.UInt16":
                        (sender as Control).Text = ((ushort)prop.GetValue(Object, null)).ToString("x4");
                        break;
                    case "System.UInt32":
                        (sender as Control).Text = ((uint)prop.GetValue(Object, null)).ToString("x8");
                        break;
                    case "System.UInt64":
                        (sender as Control).Text = ((ulong)prop.GetValue(Object, null)).ToString("x16");
                        break;
                    case "System.DateTime":
                        (sender as DateTimePicker).Value = (DateTime)prop.GetValue(Object, null);
                        break;
                    default: throw new InvalidOperationException();
                }
                onevent = false;
                if (PropertyChanged != null) PropertyChanged(this, new EventArgs());
            }
        }

        private void ShowEditDialog(object sender, EventArgs e)
        {
            PropertyInfo i = (sender as Control).Tag as PropertyInfo;
            BaseDialog dialog;

            if (i.PropertyType.IsEnum)
            {
                if (i.PropertyType.IsDefined(typeof(FlagsAttribute), true))
                    (dialog = new FlagEnumDialog(i.GetValue(Object, null))).ShowDialog();
                else
                    (dialog = new EnumDialog(i.GetValue(Object, null))).ShowDialog();
            }
            else
            {
                switch (i.PropertyType.FullName)
                {
                    case "System.Byte[]":
                        (dialog = new HexDialog(i.GetValue(Object, null) as byte[])).ShowDialog();
                        break;
                    default:
                        if (i.PropertyType.GetInterface("IList`1") != null)
                        {
                            if (Type.GetTypeCode(i.PropertyType.GetInterface("IList`1").GetGenericArguments()[0]) != TypeCode.Object)
                            {
                                (dialog = (BaseDialog)Activator.CreateInstance(typeof(PrimitiveCollectionDialog<>).MakeGenericType(i.PropertyType.GetInterface("IList`1").GetGenericArguments()[0]), (i.GetValue(Object, null)))).ShowDialog();
                            }
                            else
                            {
                                (dialog = (BaseDialog)Activator.CreateInstance(typeof(CollectionDialog<>).MakeGenericType(i.PropertyType.GetInterface("IList`1").GetGenericArguments()[0]), (i.GetValue(Object, null)))).ShowDialog();
                            }
                        }
                        else
                        {
                            (dialog = new PropertyDialog(i.GetValue(Object, null))).ShowDialog();
                        }
                        return;
                }
            }

            if (i.CanWrite)
                i.SetValue(Object, dialog.Object, null);
            else if (!i.PropertyType.IsSubclassOf(typeof(Collection<>)))
                MessageBox.Show("Readonly--Change will discard");
            if (PropertyChanged != null) PropertyChanged(this, new EventArgs());
        }

        private void ActionDispatcher(object sender, EventArgs e)
        {
            Button btn = (sender as Button);
            EventHandler h = btn.Tag as EventHandler;
            h(this, e);
        }

        public event EventHandler PropertyChanged;
    }
}
